﻿// Decompiled with JetBrains decompiler
// Type: MCM.LightInject.ILGenerator
// Assembly: MCMv5, Version=5.10.2.0, Culture=neutral, PublicKeyToken=null
// MVID: 7AB326B9-B2B4-41C2-9177-68E944071101
// Assembly location: D:\steam\steamapps\workshop\content\261550\2859238197\bin\Win64_Shipping_Client\MCMv5.dll

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;

#nullable disable
namespace MCM.LightInject
{
  [ExcludeFromCodeCoverage]
  internal class ILGenerator
  {
    private readonly ParameterExpression[] parameters;
    private readonly Stack<Expression> stack = new Stack<Expression>();
    private readonly List<LocalBuilder> locals = new List<LocalBuilder>();
    private readonly List<Expression> expressions = new List<Expression>();

    public ILGenerator(ParameterExpression[] parameters) => this.parameters = parameters;

    public Expression CurrentExpression
    {
      get
      {
        return (Expression) Expression.Block((IEnumerable<ParameterExpression>) this.locals.Select<LocalBuilder, ParameterExpression>((Func<LocalBuilder, ParameterExpression>) (l => l.Variable)).ToList<ParameterExpression>(), (IEnumerable<Expression>) new List<Expression>((IEnumerable<Expression>) this.expressions)
        {
          this.stack.Peek()
        });
      }
    }

    public void Emit(OpCode code, ConstructorInfo constructor)
    {
      if (!(code == OpCodes.Newobj))
        throw new NotSupportedException(code.ToString());
      int length = constructor.GetParameters().Length;
      this.stack.Push((Expression) Expression.New(constructor, this.Pop(length)));
    }

    public void Emit(OpCode code)
    {
      if (code == OpCodes.Ldarg_0)
        this.stack.Push((Expression) this.parameters[0]);
      else if (code == OpCodes.Ldarg_1)
        this.stack.Push((Expression) this.parameters[1]);
      else if (code == OpCodes.Ldarg_2)
        this.stack.Push((Expression) this.parameters[2]);
      else if (code == OpCodes.Ldarg_3)
        this.stack.Push((Expression) this.parameters[3]);
      else if (code == OpCodes.Ldloc_0)
        this.stack.Push((Expression) this.locals[0].Variable);
      else if (code == OpCodes.Ldloc_1)
        this.stack.Push((Expression) this.locals[1].Variable);
      else if (code == OpCodes.Ldloc_2)
        this.stack.Push((Expression) this.locals[2].Variable);
      else if (code == OpCodes.Ldloc_3)
        this.stack.Push((Expression) this.locals[3].Variable);
      else if (code == OpCodes.Stloc_0)
        this.expressions.Add((Expression) Expression.Assign((Expression) this.locals[0].Variable, this.stack.Pop()));
      else if (code == OpCodes.Stloc_1)
        this.expressions.Add((Expression) Expression.Assign((Expression) this.locals[1].Variable, this.stack.Pop()));
      else if (code == OpCodes.Stloc_2)
        this.expressions.Add((Expression) Expression.Assign((Expression) this.locals[2].Variable, this.stack.Pop()));
      else if (code == OpCodes.Stloc_3)
        this.expressions.Add((Expression) Expression.Assign((Expression) this.locals[3].Variable, this.stack.Pop()));
      else if (code == OpCodes.Ldelem_Ref)
      {
        Expression[] expressionArray = new Expression[1]
        {
          this.stack.Pop()
        };
        for (int index = 0; index < expressionArray.Length; ++index)
          expressionArray[0] = (Expression) Expression.Convert(expressionArray[index], typeof (int));
        this.stack.Push((Expression) Expression.ArrayAccess(this.stack.Pop(), expressionArray));
      }
      else if (code == OpCodes.Ldlen)
        this.stack.Push((Expression) Expression.ArrayLength(this.stack.Pop()));
      else if (code == OpCodes.Conv_I4)
        this.stack.Push((Expression) Expression.Convert(this.stack.Pop(), typeof (int)));
      else if (code == OpCodes.Ldc_I4_0)
        this.stack.Push((Expression) Expression.Constant((object) 0, typeof (int)));
      else if (code == OpCodes.Ldc_I4_1)
        this.stack.Push((Expression) Expression.Constant((object) 1, typeof (int)));
      else if (code == OpCodes.Ldc_I4_2)
        this.stack.Push((Expression) Expression.Constant((object) 2, typeof (int)));
      else if (code == OpCodes.Ldc_I4_3)
        this.stack.Push((Expression) Expression.Constant((object) 3, typeof (int)));
      else if (code == OpCodes.Ldc_I4_4)
        this.stack.Push((Expression) Expression.Constant((object) 4, typeof (int)));
      else if (code == OpCodes.Ldc_I4_5)
        this.stack.Push((Expression) Expression.Constant((object) 5, typeof (int)));
      else if (code == OpCodes.Ldc_I4_6)
        this.stack.Push((Expression) Expression.Constant((object) 6, typeof (int)));
      else if (code == OpCodes.Ldc_I4_7)
        this.stack.Push((Expression) Expression.Constant((object) 7, typeof (int)));
      else if (code == OpCodes.Ldc_I4_8)
        this.stack.Push((Expression) Expression.Constant((object) 8, typeof (int)));
      else if (code == OpCodes.Sub)
      {
        Expression right = this.stack.Pop();
        this.stack.Push((Expression) Expression.Subtract(this.stack.Pop(), right));
      }
      else
      {
        if (code == OpCodes.Ret)
          return;
        if (!(code == OpCodes.Ldnull))
          throw new NotSupportedException(code.ToString());
        this.stack.Push((Expression) Expression.Constant((object) null));
      }
    }

    public void Emit(OpCode code, LocalBuilder localBuilder)
    {
      if (code == OpCodes.Stloc)
      {
        Expression right = this.stack.Pop();
        this.expressions.Add((Expression) Expression.Assign((Expression) localBuilder.Variable, right));
      }
      else
      {
        if (!(code == OpCodes.Ldloc))
          throw new NotSupportedException(code.ToString());
        this.stack.Push((Expression) localBuilder.Variable);
      }
    }

    public void Emit(OpCode code, int arg)
    {
      if (code == OpCodes.Ldc_I4)
        this.stack.Push((Expression) Expression.Constant((object) arg, typeof (int)));
      else if (code == OpCodes.Ldarg)
        this.stack.Push((Expression) this.parameters[arg]);
      else if (code == OpCodes.Ldloc)
      {
        this.stack.Push((Expression) this.locals[arg].Variable);
      }
      else
      {
        if (!(code == OpCodes.Stloc))
          throw new NotSupportedException(code.ToString());
        Expression right = this.stack.Pop();
        this.expressions.Add((Expression) Expression.Assign((Expression) this.locals[arg].Variable, right));
      }
    }

    public void Emit(OpCode code, long arg)
    {
      if (!(code == OpCodes.Ldc_I8))
        throw new NotSupportedException(code.ToString());
      this.stack.Push((Expression) Expression.Constant((object) arg, typeof (long)));
    }

    public void PushConstantValue(object arg, Type type)
    {
      this.stack.Push((Expression) Expression.Constant(arg, type));
    }

    public void Emit(OpCode code, sbyte arg)
    {
      if (!(code == OpCodes.Ldc_I4_S))
        throw new NotSupportedException(code.ToString());
      this.stack.Push((Expression) Expression.Constant((object) (int) arg, typeof (int)));
    }

    public void Emit(OpCode code, byte arg)
    {
      if (code == OpCodes.Ldloc_S)
        this.stack.Push((Expression) this.locals[(int) arg].Variable);
      else if (code == OpCodes.Ldarg_S)
      {
        this.stack.Push((Expression) this.parameters[(int) arg]);
      }
      else
      {
        if (!(code == OpCodes.Stloc_S))
          throw new NotSupportedException(code.ToString());
        Expression right = this.stack.Pop();
        this.expressions.Add((Expression) Expression.Assign((Expression) this.locals[(int) arg].Variable, right));
      }
    }

    public void Emit(OpCode code, string arg)
    {
      if (!(code == OpCodes.Ldstr))
        throw new NotSupportedException(code.ToString());
      this.stack.Push((Expression) Expression.Constant((object) arg, typeof (string)));
    }

    public LocalBuilder DeclareLocal(Type type)
    {
      LocalBuilder localBuilder = new LocalBuilder(type, this.locals.Count);
      this.locals.Add(localBuilder);
      return localBuilder;
    }

    public void Emit(OpCode code, Type type)
    {
      if (code == OpCodes.Newarr)
        this.stack.Push((Expression) Expression.NewArrayBounds(type, this.Pop(1)));
      else if (code == OpCodes.Stelem)
      {
        Expression right = this.stack.Pop();
        Expression expression = this.stack.Pop();
        this.expressions.Add((Expression) Expression.Assign((Expression) Expression.ArrayAccess(this.stack.Pop(), expression), right));
      }
      else if (code == OpCodes.Castclass)
        this.stack.Push((Expression) Expression.Convert(this.stack.Pop(), type));
      else if (code == OpCodes.Box)
      {
        this.stack.Push((Expression) Expression.Convert(this.stack.Pop(), typeof (object)));
      }
      else
      {
        if (!(code == OpCodes.Unbox_Any))
          throw new NotSupportedException(code.ToString());
        this.stack.Push((Expression) Expression.Convert(this.stack.Pop(), type));
      }
    }

    public void Emit(OpCode code, MethodInfo methodInfo)
    {
      if (!(code == OpCodes.Callvirt) && !(code == OpCodes.Call))
        throw new NotSupportedException(code.ToString());
      Expression[] expressionArray = this.Pop(methodInfo.GetParameters().Length);
      MethodCallExpression methodCallExpression = methodInfo.IsStatic ? Expression.Call((Expression) null, methodInfo, expressionArray) : Expression.Call(this.stack.Pop(), methodInfo, expressionArray);
      if (methodInfo.ReturnType == typeof (void))
        this.expressions.Add((Expression) methodCallExpression);
      else
        this.stack.Push((Expression) methodCallExpression);
    }

    private Expression[] Pop(int numberOfElements)
    {
      Expression[] source = new Expression[numberOfElements];
      for (int index = 0; index < numberOfElements; ++index)
        source[index] = this.stack.Pop();
      return ((IEnumerable<Expression>) source).Reverse<Expression>().ToArray<Expression>();
    }
  }
}
